#include <pthread.h>

#include <iostream>
#include <sstream>

#include <pthread.h>

#include "port.h"
#include "Network.h"
#include "AgentProto.h"

#include "IListener.h"
#include "ReceiverXml.h"
#include "impl/ClientAcceptor.h"

#include "Utils.h"

// ================================================================================

class AgentListener : public net::ClientAcceptor {
  typedef std::map<std::string, Network> Networks;

public:
  AgentListener(int agentPort) {
    start(agentPort);
  }

  void serialize(net::ExtXml& xml) {
    XmlNode* root = allocNode(xml, "networks");

    for (Networks::iterator nit = _nets.begin();
         nit != _nets.end();
         ++nit) {
      root->append_node(nit->second.serialize(xml));
    }

    xml.append_node(root);
  }

protected:
  void onClientAccepted(net::Socket* sock) {
    std::cout << "A new agent is connected" << std::endl
              << "Getting the agent identifier..." << std::endl;
    
    AgentRequest msg;
    msg.type = AgentRequest::AIM_GETID;
    sock->sendMessage(msg.toVector());

    std::vector<char> agentIdV;
    sock->receiveMessage(agentIdV);

    std::string agentId = vectorToString(agentIdV);
    std::cout << "Handling network " << agentId << std::endl;

    _nets.insert(std::make_pair(agentId, Network()));

    new AgentHandler(agentId, sock, this);
  }

private:

  class AgentHandler {
  public:
    AgentHandler(const std::string& id, net::Socket* sock, AgentListener* listener) {
      _id = id;
      _sock = sock;
      _listener = listener;

      pthread_t th;
      pthread_create(&th, NULL, agentHandlerThread, this);
    }

  private:
    void handleAgent() {
      try {
        for (;;) {
          std::cout << "Sending network graph request..." << std::endl;

          AgentRequest msg;
          msg.type = AgentRequest::AIM_GETGRAPH;
          _sock->sendMessage(msg.toVector());

          std::stringstream stream;
          std::vector<char> data;

          _sock->receiveMessage(data);
          stream.str(vectorToString(data));

          std::cout << "Received network graph. " << std::endl;

          text_iarchive ar(stream);
          _listener->_nets[_id].clear();
          ar >> _listener->_nets[_id];

          sleep(60);
        } 
      } catch (std::exception& e) {
        std::cout << "Caught an exception: " << e.what() << "while handling the client " << _id << ". Exiting..." << std::endl;
      }
    }

    static void* agentHandlerThread(void* arg) {
      AgentHandler* self = (AgentHandler*)arg;

      self->handleAgent();

      delete self;

      return NULL;
    }

  private:

    std::string _id;
    net::Socket* _sock;
    AgentListener* _listener;
  };  

private:
  Networks _nets;
};

// --------------------------------------------------------------------------------

class ViewerHandler : public net::IListener {
public:
  ViewerHandler(AgentListener* al) {
    _receiver.subscribe(this);
    _receiver.start(NGU_VIEWER_PORT);
    _al = al;
  }

  net::ExtXml onXmlReceived(net::ExtXml const& rx_message) {
    net::ExtXml xml;
    _al->serialize(xml);

    return xml;
  }

private:
  net::ReceiverXml _receiver;
  AgentListener* _al;
};

// --------------------------------------------------------------------------------

int main(int argc, char* argv[]) {
  int agentPort = NGU_AGENT_PORT;

  if (argc >= 2) {
    agentPort = atoi(argv[1]);
  }

  AgentListener agentListener(agentPort);
  ViewerHandler viewerHandler(&agentListener);

  for (;;) {
    sleep(120);    // Can it be avoided since there's no main loop to enter any more?..
  }
}
